import Taro from '@tarojs/taro'
import { SelectorQuery } from '@tarojs/taro/types/index'
const ENV = Taro.getEnv()

const countDown = (time: number = 30, cb: any) => {
  let clearFn: any
  let newTime = time * 1000
  function count() {
    cb(newTime / 1000)
    clearFn = setTimeout(function () {
      newTime -= 1000
      if (newTime > -1) {
        count()
      } else {
        newTime = 0
        clearTimeout(clearFn)
      }
    }, 1000)
  }
  count()
}

function formatNumber (n) {
  n = n.toString()
  return n[1] ? n : '0' + n
}

function countDownFormat (timestamp: number | string, type = 0, is_need_msecond = false) {
  // console.log(date)
  var hour = parseInt(timestamp / 1000 / 60 / 60 % 24)
  var minute = parseInt(timestamp / 1000 / 60 % 60)
  var second = parseInt(timestamp / 1000 % 60)
  var msecond = parseInt(timestamp % 1000) / 100
  let resultArray;
  !is_need_msecond ? resultArray = [hour, minute, second].map(formatNumber) : resultArray = [hour, minute, second, msecond].map((item: any, index) => {
    item = item.toString()
    return item[1] ? item : index !== 3 ? '0' + item : item
  })
  if (type === 0) {
    return !is_need_msecond ? `${resultArray[0]}:${resultArray[1]}:${resultArray[2]}` : `${resultArray[0]}:${resultArray[1]}:${resultArray[2]} ${resultArray[3]}`
  } else {
    return resultArray
  }
}

function objectMerge(target: object, source: object) {
  if (typeof target !== 'object') {
    target = {}
  }
  if (Array.isArray(source)) {
    return source.slice()
  }
  Object.keys(source).forEach(property => {
    const sourceProperty = source[property]
    if (typeof sourceProperty === 'object') {
      target[property] = objectMerge(target[property], sourceProperty)
    } else {
      target[property] = sourceProperty
    }
  })
  return target
}

function isString(str: string | object) {
  if (typeof str === 'string' || str instanceof String) {
    return true
  }
  return false
}

function isArray(arg:  string | object) {
  if (typeof Array.isArray === 'undefined') {
    return Object.prototype.toString.call(arg) === '[object Array]'
  }
  return Array.isArray(arg)
}

function isObject(obj:  string | object) {
  return Object.prototype.toString.call(obj) === '[object Object]'
}

function isFunction<T>(obj: T): boolean {
  return Object.prototype.toString.call(obj) === '[object Function]'
}

if (!Object.entries) {
  Object.entries = function(obj){
    var ownProps = Object.keys(obj),
        i = ownProps.length,
        resArray = new Array(i) // preallocate the Array
    while (i--)
      resArray[i] = [ownProps[i], obj[ownProps[i]]]

    return resArray
  }
}

// 链接拼接参数处理
function concatUrl(url: string, params: string | object = '') {
  let queryString: string = ''
  if (!params) {
    return url
  }
  if (url.indexOf('?') > -1) {
    queryString += '&'
  } else {
    queryString += '?'
  }
  if (isObject(params)) {
    let incise: string[] = []
    for(let [k, v] of Object.entries(params)) {
      incise.push(`${k}=${v}`)
    }
    params = incise.join('&')
  }
  // console.log(params, 2)
  queryString += params
  return url + queryString
}

// 手机号码转为成*显示 13286608820 => 132****8820
function encryptMobile(mobile: string) {
  if (!mobile) return ''
  return mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
}

/**
 * GET请求 query处理
 * @param {object} data
 * @param {('object' | 'string')} type
 * @default ['object']
 * @returns {(object | string)} encodeeurl
 */

function encodeURI(data: object, type: 'object' | 'string' = 'object'): string | object {
  if (type === 'string') return encodeURIComponent(JSON.stringify(data))
  return {
    query: encodeURIComponent(JSON.stringify(data))
  }
}

function throttle (func, wait = 50) {
  // 上一次执行该函数的时间
  let lastTime = 0
  return function(...args) {
    // 当前时间
    let now = +new Date()
    // 将当前时间和上一次执行函数时间对比
    // 如果差值大于设置的等待时间就执行函数
    if (now - lastTime > wait) {
      lastTime = now
      func.apply(this, args)
    }
  }
}


/**
 * deboundce
 * @param {Function} fn callback
 * @param {any} time @default 100
 * @returns {Function} context
 */
function debounce(fn: Function, delay: any = 50) {
  let timeout
  return function() {
    const functionCall = () => fn.apply(this, arguments)
    clearTimeout(timeout)
    timeout = setTimeout(functionCall, delay)
  }
}

function delay (delayTime = 500): Promise<null> {
  return new Promise(resolve => {
    if ([Taro.ENV_TYPE.WEB, Taro.ENV_TYPE.SWAN].includes(ENV)) {
      setTimeout(() => {
        resolve()
      }, delayTime)
      return
    }
    resolve()
  })
}

function delayQuerySelector (
  self,
  selectorStr: string,
  delayTime = 500
): Promise<Array<any>> {
  const $scope = ENV === Taro.ENV_TYPE.WEB ? self : self
  const selector: SelectorQuery = Taro.createSelectorQuery().in($scope)

  return new Promise(resolve => {
    delay(delayTime).then(() => {
      selector
        .select(selectorStr)
        .boundingClientRect()
        .exec((res: Array<any>) => {
          resolve(res)
        })
    })
  })
}

function uuid (len = 8, radix = 16): string {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
  const value: string[] = []
  let i = 0
  radix = radix || chars.length

  if (len) {
    // Compact form
    for (i = 0; i < len; i++) value[i] = chars[0 | (Math.random() * radix)]
  } else {
    // rfc4122, version 4 form
    let r

    // rfc4122 requires these characters
    /* eslint-disable-next-line */
    value[8] = value[13] = value[18] = value[23] = '-'
    value[14] = '4'

    // Fill in random data.  At i==19 set the high bits of clock sequence as
    // per rfc4122, sec. 4.1.5
    for (i = 0; i < 36; i++) {
      if (!value[i]) {
        r = 0 | (Math.random() * 16)
        value[i] = chars[i === 19 ? (r & 0x3) | 0x8 : r]
      }
    }
  }

  return value.join('')
}

function formatToAsterisk(str: string):string {
  return str.replace(/(\w{3,4})\w{8}(\w{4,5})/, '$1********$2')
}

// 判断是否为手机号码
function isPhone(str: string) {
  return /1\d{10}/g.test(str)
}

// 判断是否为5位数字验证码
function isCode(str: string) {
  return /\d{5}/g.test(str)
}

// 清除空白字符
function trimAll(str: string) {
  return str.replace(/\s*/g, '')
}

// 返回资源地址
function staticUrl(path: string) {
  return SOURCES_PATH + path
}

/**
 * 加法函数，用来得到精确的加法结果<br>
 * javascript的加法结果会有误差，在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
 *
 * @param {number} arg1
 * @param {number} arg2
 * @returns {number} arg1加上arg2的精确结果
 * @example
 *
 * accAdd(0.1, 0.2)
 * // => 0.3
 */
function accAdd(arg1, arg2) {
  var r1;
  var r2;
  var m;
  var c;

  try {
    r1 = arg1.toString().split('.')[1].length;
  } catch (e) {
    r1 = 0;
  }
  try {
    r2 = arg2.toString().split('.')[1].length;
  } catch (e) {
    r2 = 0;
  }
  c = Math.abs(r1 - r2);
  m = Math.pow(10, Math.max(r1, r2));
  if (c > 0) {
    var cm = Math.pow(10, c);
    if (r1 > r2) {
      arg1 = Number(arg1.toString().replace('.', ''));
      arg2 = Number(arg2.toString().replace('.', '')) * cm;
    } else {
      arg1 = Number(arg1.toString().replace('.', '')) * cm;
      arg2 = Number(arg2.toString().replace('.', ''));
    }
  } else {
    arg1 = Number(arg1.toString().replace('.', ''));
    arg2 = Number(arg2.toString().replace('.', ''));
  }
  return (arg1 + arg2) / m;
}

/**
 * 减法函数，用来得到精确的减法结果<br>
 * javascript的减法结果会有误差，在两个浮点数相减的时候会比较明显。这个函数返回较为精确的减法结果。
 *
 * @param {number} arg1
 * @param {number} arg2
 * @returns {number} arg1减去arg2的精确结果
 * @example
 *
 * accDiv(0.3, 0.2)
 * // => 0.1
 */
function accSub(arg1, arg2) {
  var r1;
  var r2;
  var m;
  var n;

  try {
    r1 = arg1.toString().split('.')[1].length;
  } catch (e) {
    r1 = 0;
  }
  try {
    r2 = arg2.toString().split('.')[1].length;
  } catch (e) {
    r2 = 0;
  }
  m = Math.pow(10, Math.max(r1, r2)); // last modify by deeka //动态控制精度长度
  n = (r1 >= r2) ? r1 : r2;
  return ((arg1 * m - arg2 * m) / m).toFixed(n);
}

/**
 * 乘法函数，用来得到精确的乘法结果<br>
 * javascript的乘法结果会有误差，在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
 *
 * @param {number} arg1
 * @param {number} arg2
 * @returns {number} arg1乘以arg2的精确结果
 * @example
 *
 * accMul(0.222, 0.3333)
 * // => 0.0739926
 */
function accMul(arg1, arg2) {
  var m = 0;
  var s1 = arg1.toString();
  var s2 = arg2.toString();

  try {
    m += s1.split('.')[1].length;
  } catch (e) {
  }
  try {
    m += s2.split('.')[1].length;
  } catch (e) {
  }
  return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m);
}

function pathQueryToJson(str: string) {
  let arr = str.split('&')
  let newObj = {}
  for(const fields of arr) {
    const splitField = fields.split('=')
    newObj[splitField[0]] = splitField[1]
  }
  return newObj
}


export {
  pathQueryToJson,
  countDown,
  objectMerge,
  isString,
  isArray,
  isFunction,
  isObject,
  concatUrl,
  encryptMobile,
  encodeURI,
  debounce,
  delay,
  delayQuerySelector,
  uuid,
  formatToAsterisk,
  isPhone,
  isCode,
  trimAll,
  staticUrl,
  countDownFormat,
  accAdd,
  accSub,
  accMul,
	throttle
}
